Skip to content

Tor support#1336

Open
Jem256 wants to merge 12 commits intojamaljsr:masterfrom
Jem256:tor-support
Open

Tor support#1336
Jem256 wants to merge 12 commits intojamaljsr:masterfrom
Jem256:tor-support

Conversation

@Jem256
Copy link
Contributor

@Jem256 Jem256 commented Dec 10, 2025

Closes #219

Description

This PR adds Tor support for both Lightning Network and Bitcoin nodes, enabling users to test and use .onion services for connectivity. Provides a convenient setup for experimenting with Tor-based networks in a local or containerized environment.

Tor Support Implementation Checklist

  • Tor daemon runs per node
  • Tor support implemented for Bitcoin (bitcoind), Lightning (LND, c-lightning, Eclair and Terminal )
  • Added a network-level toggle to enable or disable Tor for all supported nodes before starting the network
  • Tor configuration persists correctly across network start/stop cycles
  • Docker startup commands are generated dynamically based on Tor enablement
  • Added an onion icon to the network chart to visually identify Tor-enabled nodes
  • Add a per-node enable Tor option in the node submenu (trigger network restart)

Steps to Test

Flow 1: Create network → toggle Tor ON/OFF for all nodes → start network
Flow 2: Create network → start network (TOR OFF) → modify individual node to enable Tor → restart node

TODO

Push new images to Docker Hub for all node versions currently supported by Polar

Screenshots

Screenshot from 2026-01-11 18-35-45

@Jem256 Jem256 force-pushed the tor-support branch 3 times, most recently from 99f0712 to 2d15a97 Compare December 16, 2025 20:56
@codecov
Copy link

codecov bot commented Dec 16, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (711755f) to head (a245860).

Additional details and impacted files
@@            Coverage Diff             @@
##            master     #1336    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files          211       212     +1     
  Lines         7014      7267   +253     
  Branches      1398      1423    +25     
==========================================
+ Hits          7014      7267   +253     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Jem256 Jem256 marked this pull request as ready for review December 21, 2025 21:08
@Jem256
Copy link
Contributor Author

Jem256 commented Dec 21, 2025

Hey @jamaljsr @Abdulkbk, I would appreciate a review when you get time.

@jamaljsr
Copy link
Owner

Hey @Jem256 This is a fantastic feature. We've received many requests for Tor support over the year. Thanks you so much for tackling this.

I've done some functional testing and have a bit of feedback/questions:

  1. I had to rebuild the docker images locally in order to get the nodes to start up. This is fine, but we'll need to also be sure to push new images to Docker Hub for all node versions currently supported by Polar. It would be good to add this as a TODO (for me) in the PR description since this feature won't work without doing this.
  2. You currently have Tor support for Bitcoin Core, LND, and Core Lightning. Do you plan to add Terminal and Eclair, or is there some explanation for why these cannot be included?
  3. What is the best way to confirm that the Tor connections are functioning properly for each node? Are there any tools/commands we can run from outside the network to verify it works? I can see the services running in the logs, but are the LN/BTC nodes actually using tor?
  4. When I add multiple Bitcoin Core nodes, they are not syncing their chains with each other. When i mine blocks on one node, the block height on the others do not increase. I also don't see the onion address in the sidebar for these.
  5. The Tor toggle in the NetworkActions toolbar feels too prominent for it's potential use. Since I suspect this feature will be used by a minority of users, I would prefer to place this toggle inside of the 3-dot dropdown menu instead. It's shouldn't be front & center next to the Start button.
  6. I like the onion icons on each node. They look great when there are no channels, but once a node has channels, the placement of the icon feels off.
    image

@Abdulkbk
Copy link
Contributor

Thanks for the PR @Jem256. I also did some functional tests.

  1. I run a test with 2 bitcoin nodes, and they're not syncing. I run bitcoin-cli getpeerinfo, and it seems they don't see each other on the network.
  2. I noticed that creating a channel when TOR is enabled is not possible, complains about insufficient funds even though the option Deposit enough sats to carol to fund the channel is selected (works when TOR is not enabled).
  3. When a node is added to the network, if TOR is enabled globally, the new node should, by default, allow TOR.

Questions:

  1. I noticed that when I connect an LND and a CLN as peers, LND still sees the cleartext address of the CLN lncli listpeers. Do you know if this is the correct behaviour?.

I will look deeply into the code but for now these are my observations.

@Jem256
Copy link
Contributor Author

Jem256 commented Jan 5, 2026

Hey @Jem256 This is a fantastic feature. We've received many requests for Tor support over the year. Thanks you so much for tackling this.

I've done some functional testing and have a bit of feedback/questions:

  1. I had to rebuild the docker images locally in order to get the nodes to start up. This is fine, but we'll need to also be sure to push new images to Docker Hub for all node versions currently supported by Polar. It would be good to add this as a TODO (for me) in the PR description since this feature won't work without doing this.
  2. You currently have Tor support for Bitcoin Core, LND, and Core Lightning. Do you plan to add Terminal and Eclair, or is there some explanation for why these cannot be included?
  3. What is the best way to confirm that the Tor connections are functioning properly for each node? Are there any tools/commands we can run from outside the network to verify it works? I can see the services running in the logs, but are the LN/BTC nodes actually using tor?
  4. When I add multiple Bitcoin Core nodes, they are not syncing their chains with each other. When i mine blocks on one node, the block height on the others do not increase. I also don't see the onion address in the sidebar for these.
  5. The Tor toggle in the NetworkActions toolbar feels too prominent for it's potential use. Since I suspect this feature will be used by a minority of users, I would prefer to place this toggle inside of the 3-dot dropdown menu instead. It's shouldn't be front & center next to the Start button.
  6. I like the onion icons on each node. They look great when there are no channels, but once a node has channels, the placement of the icon feels off.
    image
  1. This has been done.
  2. I have added eclair support. However, I have not found documentation on Tor regarding Terminal. Is the implementation similar to LND?
  3. So far, I have been using the logs and respective node CLI to check if a Tor address is advertised. I shall add another way to test this before the next review.
  4. Since bitcoin core doesn't announce local addresses on regtest, I am still trying to figure out how we get addresses for these nodes. I think this is what is causing the issue.
    5 & 6 . Will improve these

@Jem256
Copy link
Contributor Author

Jem256 commented Jan 5, 2026

  1. I noticed that when I connect an LND and a CLN as peers, LND still sees the cleartext address of the CLN lncli listpeers. Do you know if this is the correct behaviour?.

This is not the correct behaviour. I realised my current implementation is hybrid instead of Tor only. I'll improve this

@jamaljsr
Copy link
Owner

jamaljsr commented Jan 6, 2026

2. I have added eclair support. However, I have not found documentation on Tor regarding Terminal. Is the implementation similar to LND?

Terminal is just a wrapper around LND (plus the other daemons). LND is running inside of the Terminal process. So all of the LND config flags works for Terminal. You just have to add an lnd. prefix to the flag name. Ex: --tor.active becomes --lnd.tor.active.

@Jem256 Jem256 marked this pull request as draft January 10, 2026 14:09
@Jem256 Jem256 force-pushed the tor-support branch 3 times, most recently from 476c6b1 to eb45f9e Compare January 11, 2026 15:15
@Jem256 Jem256 marked this pull request as ready for review February 18, 2026 13:07
@greptile-apps
Copy link

greptile-apps bot commented Feb 18, 2026

Greptile Summary

This PR adds comprehensive Tor support across all node types in Polar (bitcoind, LND, Core Lightning, Eclair, litd), enabling users to test .onion services in their local Lightning Network simulations. The implementation spans Docker configuration, state management, UI components, and service layer changes.

Key changes:

  • Each Docker image now includes the Tor daemon, started conditionally via ENABLE_TOR environment variable in the entrypoint scripts
  • New enableTor property on LightningNode and BitcoinNode types, persisted across network save/load cycles
  • Network-wide Tor toggle (in the dropdown menu) and per-node Tor toggle (in context menu and actions tab) with confirmation modals and restart handling
  • Dynamic command generation via applyTorFlags / getEffectiveCommand to inject/remove Tor-specific CLI flags
  • Onion icon overlay on the network designer chart for Tor-enabled nodes
  • Updated ConnectTab for both bitcoin and lightning nodes to display .onion P2P addresses
  • Moved connectPeers for bitcoin nodes into the store layer with Tor-aware address resolution
  • Good test coverage with new test files and additions to existing suites

Issues found:

  • Missing await on actions.save() in toggleTorForNode creates a race condition where saveComposeFile may read stale state
  • The -onlynet=onion bitcoind flag will prevent mixed-mode peer connectivity (Tor + non-Tor nodes in the same network)
  • Unconditional usermod -a -G debian-tor in entrypoint scripts (bitcoind, lnd, clightning, litd) will fail with set -e if the group doesn't exist, unlike eclair which has a guard
  • Inconsistent /var/lib/tor permissions in litd entrypoint (chmod 755 vs chmod 700 everywhere else)
  • Bitcoin ConnectTab may display undefined for P2P host before node info loads

Confidence Score: 3/5

  • Well-structured feature with good test coverage, but several issues in Docker entrypoints and a missing await could cause runtime failures
  • The PR is architecturally sound and follows existing patterns well. However, the missing await on actions.save() is a real bug that could lead to stale state, the -onlynet=onion flag may break mixed-mode networks, and the unconditional usermod calls in 4 of 5 entrypoints could cause container startup failures. These issues are fixable but should be addressed before merging.
  • Pay close attention to src/store/models/network.ts (missing await), src/utils/network.ts (-onlynet=onion flag), docker/bitcoind/docker-entrypoint.sh, docker/lnd/docker-entrypoint.sh, docker/clightning/docker-entrypoint.sh, and docker/litd/docker-entrypoint.sh (unconditional usermod and inconsistent permissions)

Important Files Changed

Filename Overview
src/store/models/network.ts Core Tor toggle logic for network and individual nodes. Missing await on actions.save() in toggleTorForNode (line 1373) creates a race condition. Moved connectPeers and getInfo out of bitcoin node startup and added Tor-aware delays.
src/utils/network.ts Added applyTorFlags, getEffectiveCommand, supportsTor utilities. The -onlynet=onion flag for bitcoind may prevent mixed-mode peer connectivity in the Docker network.
src/store/models/bitcoin.ts Added connectAllPeers thunk and getNetworkInfo integration. The peer connection logic correctly resolves onion addresses when both peers have Tor enabled, falling back to Docker hostnames otherwise.
src/lib/docker/composeFile.ts Added Tor flag application and ENABLE_TOR env variable to all node types (bitcoind, LND, CLN, Eclair, litd). Consistent pattern across all add* methods.
src/lib/bitcoin/bitcoind/bitcoindService.ts Added getNetworkInfo method extracting onion addresses from localaddresses. Changed connectPeers to accept explicit peer addresses instead of using node.peers.
src/lib/bitcoin/notImplementedService.ts Added getNetworkInfo stub, but connectPeers signature doesn't match the updated BitcoinService interface (missing peerAddresses parameter).
src/components/designer/bitcoin/ConnectTab.tsx Uses dynamic P2P host from node state (onion or clearnet). The p2pHost value can be undefined before node info loads, which would pass "undefined" to the copy icon.
src/components/designer/lightning/ConnectTab.tsx Added p2pUriExternal function that returns the internal Tor rpcUrl when Tor is enabled, or the clearnet URI otherwise. Applied consistently across all lightning implementations.
src/components/common/TorButton.tsx New component providing per-node Tor enable/disable with confirmation modals and restart warnings. Clean implementation with proper state handling.
src/components/network/NetworkActions.tsx Added network-wide Tor toggle switch in the dropdown menu. Correctly disables the switch when the network is started. Uses supportsTor to determine eligibility.
docker/bitcoind/docker-entrypoint.sh Added Tor daemon setup with hidden service. Unconditional usermod may fail if debian-tor group doesn't exist, unlike the eclair entrypoint which has a guard.
docker/lnd/docker-entrypoint.sh Added Tor daemon setup with hidden service for LND P2P and REST ports. Same unconditional usermod issue as bitcoind entrypoint.
docker/eclair/docker-entrypoint.sh Added Tor setup with proper group existence guard. Correctly skips public IP announcement when Tor is enabled. Best-implemented entrypoint of the set.
docker/litd/docker-entrypoint.sh Added Tor setup for litd. Uses chmod 755 on /var/lib/tor while all others use chmod 700 — inconsistent and less secure.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User toggles Tor] --> B{Network-wide or Per-node?}
    B -->|Network-wide| C[NetworkActions: handleTorToggle]
    B -->|Per-node| D[TorButton: showConfirmModal]
    
    C --> E[network.toggleTorForNetwork]
    E --> F[setAllNodesTor - set enableTor on all nodes]
    F --> G[save network state]
    G --> H[saveComposeFile - regenerate docker-compose.yml]
    
    D --> I[network.toggleTorForNode]
    I --> J{Node started?}
    J -->|Yes| K[toggleNode - stop node]
    J -->|No| L[setNodeTor - set enableTor]
    K --> L
    L --> M[save network state]
    M --> N[saveComposeFile]
    N --> O{Was started?}
    O -->|Yes| P[toggleNode - restart node]
    O -->|No| Q[Done]
    P --> Q
    
    H --> R[ComposeFile: applyTorFlags + ENABLE_TOR env]
    N --> R
    R --> S[Docker entrypoint: conditionally start Tor daemon]
    S --> T{ENABLE_TOR=true?}
    T -->|Yes| U[Start Tor + Hidden Service]
    T -->|No| V[Skip Tor setup]
    
    style A fill:#f9f,stroke:#333
    style Q fill:#9f9,stroke:#333
    style U fill:#ff9,stroke:#333
Loading

Last reviewed commit: 759415b

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

49 files reviewed, 7 comments

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link

greptile-apps bot commented Feb 18, 2026

Additional Comments (1)

src/lib/bitcoin/notImplementedService.ts
Signature mismatch with BitcoinService interface

The BitcoinService interface (in src/types/index.ts:160) now requires connectPeers(node: BitcoinNode, peerAddresses: string[]), but this implementation still uses the old single-parameter signature. TypeScript won't flag this at compile time due to function parameter bivariance, but the signature should be updated for consistency and to match the interface contract.

  connectPeers(node: BitcoinNode, peerAddresses: string[]): Promise<void> {
    throw new Error(`connectPeers is not implemented for ${node.implementation} nodes`);
  }

@Jem256
Copy link
Contributor Author

Jem256 commented Feb 19, 2026

I had to rebuild the docker images locally in order to get the nodes to start up. This is fine, but we'll need to also be sure to push new images to Docker Hub for all node versions currently supported by Polar. It would be good to add this as a TODO (for me) in the PR description since this feature won't work without doing this.

This was done.

You currently have Tor support for Bitcoin Core, LND, and Core Lightning. Do you plan to add Terminal and Eclair, or is there some explanation for why these cannot be included?

All nodes are now supported. However, opening channels in Eclair is crushing. Related to #1212 This is something i am investigating.

When I add multiple Bitcoin Core nodes, they are not syncing their chains with each other. When i mine blocks on one node, the block height on the others do not increase. I also don't see the onion address in the sidebar for these.

Tor processes are slow and this is what affects the syncing. I have increased the delay time on monitorstartup to somewhat improve this especially for peer connections. However, block propagation via tor is still slow. It eventually resolves itself but I dont know how to make it better

The Tor toggle in the NetworkActions toolbar feels too prominent for it's potential use. Since I suspect this feature will be used by a minority of users, I would prefer to place this toggle inside of the 3-dot dropdown menu instead. It's shouldn't be front & center next to the Start button.

This was done.

I like the onion icons on each node. They look great when there are no channels, but once a node has channels, the placement of the icon feels off.

This was improved. Channels don't throw the icon placement off anymore.

What is the best way to confirm that the Tor connections are functioning properly for each node? Are there any tools/commands we can run from outside the network to verify it works? I can see the services running in the logs, but are the LN/BTC nodes actually using tor?

  1. lncli --rpcserver=<grpc host> --tlscertpath=<> --macaroonpath=<> getinfo
    Expected: find a .onion address only in the addresses field.
    listpeers should show .onion address for all outbound connections
    The premise of getinfo and looking at peers is to confirm that connections are no longer via docker bridge but tor socks and tor is actually being used by the nodes through published addresses.

  2. torify nc -zv example.onion 9735 (corresponding port depending on implementation)
    Expected: Connection to example.onion (127.42.42.0) 9735 port [tcp/*] succeeded! This test verifies external reachability of the ports outside the network.

    @jamaljsr

@Jem256
Copy link
Contributor Author

Jem256 commented Feb 19, 2026

I run a test with 2 bitcoin nodes, and they're not syncing. I run bitcoin-cli getpeerinfo, and it seems they don't see each other on the network.

This is now working. However, syncing is a bit slow because tor is slow. I'd appreciate suggestions on how to improve this

I noticed that creating a channel when TOR is enabled is not possible, complains about insufficient funds even though the option Deposit enough sats to carol to fund the channel is selected (works when TOR is not enabled).

This is now working.

When a node is added to the network, if TOR is enabled globally, the new node should, by default, allow TOR.

This was implemented.

I noticed that when I connect an LND and a CLN as peers, LND still sees the cleartext address of the CLN lncli listpeers. Do you know if this is the correct behaviour?.

Outbound peers ("inbound": false) show .onion addresses with "network": "onion"
addrbind for outbound peers is 127.0.0.1:port, meaning the connection left through the local Tor SOCKS proxy.
Inbound peers show 127.0.0.1:port address because Tor is anonymizing the connecting peer. When a remote node connects to you via Tor, the connection arrives at your machine as a local TCP socket.
What we don't expect to see are docker bridge IPs (eg 172.18.0.7:port)

cc @Abdulkbk

Copy link
Contributor

@Abdulkbk Abdulkbk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good 👍.

I have tested with bitcoind, lnd, and cln. I've not tested with eclair yet. I left some feedback.

- Implement toggleTorForNetwork action to update all LND nodes
- Create applyTorFlags utility to manage Tor command flags
- Update Advanced Options to display effective command with Tor flags
- Tor flags added dynamically during compose generation
- ui for enable tor for all nodes
Jem256 added 11 commits March 8, 2026 12:15
Extends existing Tor functionality to Bitcoin Core nodes. Refactors
applyTorFlags to handle multiple implementations and updates Docker
compose generation to conditionally apply bitcoind Tor flags based
on node.enableTor setting.
- Extends existing Tor functionality to Core lightning  nodes.
- update getInfo service to fetch tor address
Add test coverage for Tor network toggle functionality, environment variables,
and edge cases for various node types.
Add ability to enable/disable Tor for individual nodes through context menu.
When toggling Tor on a running node, automatically stops, updates settings,
and restarts the node. Includes UI updates to show Tor status with icon.
- Add supportsTor() helper to check Tor compatibility
- Show "not supported" message for tap nodes
- Hide Tor menu items for unsupported nodes
- Simplify Tor-related functions using new helper
Extends existing Tor functionality to eclair lightning  nodes.
Extends existing Tor functionality to terminal lightning  nodes.
…k toggle when started

- New nodes inherit network's Tor setting when added via drag-and-drop
- Network-wide Tor toggle disabled when network is running
- Adds getNetworkInfo() method to retrieve network details from bitcoind nodes,
  including p2pHost detection for both clearnet and Tor addresses. Formats long
  Tor v3 addresses using ellipsis for better UI display. Updates store models and
  UI components to display connection details. Updates connectPeers to use onion
  addresses
@Jem256 Jem256 requested a review from Abdulkbk March 10, 2026 07:39
Copy link
Contributor

@Abdulkbk Abdulkbk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work so far, getting there...

I tested the functionality and observed some race conditions between Tor (bootstrapping) and the startup of LN nodes. This occasionally prevents nodes from discovering or connecting to peers. For instance, I ran with three LND nodes, and sometimes peers connect successfully, while other times they do not. I suspect we need to ensure Tor has fully started and bootstrapped in docker-entry.sh before launching the LN nodes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: Tor support

3 participants